package com.zxjbyte.yiyi.framework.common.util;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.net.URLEncodeUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.google.common.collect.Maps;
import com.zxjbyte.yiyi.framework.common.constant.Constant;
import com.zxjbyte.yiyi.framework.common.constant.StringPool;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import org.springframework.http.HttpHeaders;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;
import java.util.stream.Collectors;

import static com.zxjbyte.yiyi.framework.common.constant.StringPool.*;

/**
 * 扩展 cn.hutool.extra.servlet.ServletUtil
 * @Author zhangxingjia
 * @Date 2023/3/10 16:42
 * @Version: 1.0
 */
@UtilityClass
public class ServletXUtil extends ServletUtil {

    private final PathMatcher PATH_MATCHER = new AntPathMatcher();

    /**
     * 判断请求路径是否匹配给定的路径模式列表
     * @param path 请求路径
     * @param patterns 路径模式列表（支持 Ant 风格）
     * @return true 表示匹配，过滤器可以跳过该请求
     */
    public boolean matchesAnyPattern(String path, List<String> patterns){
        if (patterns == null || patterns.isEmpty() || path == null) {
            return false;
        }
        for (String pattern : patterns) {
            if (PATH_MATCHER.match(pattern, path)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取标准化的请求路径（推荐）
     */
    public String getRequestPath(HttpServletRequest request) {
        return getRequestPath(request, true);
    }

    /**
     * 获取请求路径（可选标准化）
     *
     * @param request HTTP请求
     * @param normalize 是否标准化路径 （去多斜杠、补前缀等）
     * @return 请求路径
     */
    public String getRequestPath(HttpServletRequest request, boolean normalize) {
        String requestUri  = request.getRequestURI();
        String contextPath = request.getContextPath();

        String path = URLXUtil.getPath(requestUri);
        if (StrUtil.isNotBlank(contextPath) && !"/".equals(contextPath) && path.startsWith(contextPath)) {
            path = path.substring(contextPath.length());
        }
        if (!normalize) {
            return path;
        }
        if (StrUtil.isBlank(path)) {
            return "/";
        }
        // 确保以 / 开头
        if (!path.startsWith("/")) {
            path = "/" + path;
        }

        // 去掉多余的连续斜杠
        while (path.contains("//")) {
            path = path.replace("//", "/");
        }
        return path;
    }

    /**
     * 获取URL后面的参数转Map
     * @param url
     * @return
     */
    public Map<String, List<String>> getParamsFromUrl(String url) {
        Map<String, List<String>> map = null;
        if (url != null && url.contains(QUESTION_MARK)) {
            map = queryStringToMap(StrUtil.subAfter(url, QUESTION_MARK, false));
        }
        return map == null ? Maps.newHashMap() : map;
    }

    /**
     * URL ? 后面参数转Map
     * @param queryString
     * @return
     */
    public Map<String, List<String>> queryStringToMap(String queryString) {
        if (StrUtil.isBlank(queryString)) {
            return null;
        }
        queryString = URLUtil.decode(queryString, Constant.CHARSET_UTF_8);
        Map<String, List<String>> map = Maps.newLinkedHashMap();
        List<String> params = StrUtil.splitTrim(queryString, AMPERSAND);
        for (String param : params) {
            String[] kv = param.split(EQUALS);
            map.computeIfAbsent(kv[0], func -> new ArrayList<>()).add(kv[1]);
        }
        return map;
    }

    /**
     * 获取form表单提交的参数值
     * @param name
     * @return
     */
    public String getParameter(String name){
        return getParameter(name, null);
    }

    /**
     * 获取form表单提交的参数值
     * @param name
     * @param defaultValue
     * @return
     */
    public String getParameter(String name, String defaultValue){
        HttpServletRequest request = getRequest();
        if(request != null){
            return StrUtil.nullToDefault(request.getParameter(name), defaultValue);
        }
        return defaultValue;
    }

    /**
     * 获取 HttpServletRequest
     *
     * @return {HttpServletRequest}
     */
    public HttpServletRequest getRequest() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        return (requestAttributes == null) ? null : ((ServletRequestAttributes) requestAttributes).getRequest();
    }

    /**
     * 获取 HttpServletResponse
     *
     * @return {HttpServletResponse}
     */
    public HttpServletResponse getResponse() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        return (requestAttributes == null) ? null : ((ServletRequestAttributes) requestAttributes).getResponse();
    }

    /**
     * 获取浏览器UA
     * @param request 请求
     * @return ua
     */
    public String getUserAgent(HttpServletRequest request) {
        return getHeaderIgnoreCase(request, HttpHeaders.USER_AGENT);
    }

    /**
     * 是否为JSON格式请求
     * @param request
     * @return
     */
    public boolean isJsonRequest(ServletRequest request) {
        return isJsonMime(request.getContentType());
    }

    /**
     * JSON MIME examples:
     *    application/json
     *    application/json; charset=UTF8
     *    APPLICATION/JSON
     *    application/vnd.company+json
     *    "* / *" is also default to JSON
     * @param contentType
     * @return
     */
    public boolean isJsonMime(String contentType) {
        String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$";
        return contentType != null && (contentType.matches(jsonMime) || contentType.equals("*/*"));
    }

    /**
     * 输出附件
     * @param response
     * @param filename 文件名
     * @param content 附件内容
     */
    @SneakyThrows
    public void writeAttachment(HttpServletResponse response, String filename, byte[] content){
        response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + URLEncodeUtil.encode(filename));
        response.setContentType(FileUtil.getMimeType(filename));
        response.setContentLength(content.length);
        response.setCharacterEncoding(Constant.UTF_8);
        // 输出附件
        IoUtil.write(response.getOutputStream(), false, content);
    }

    /**
     * 获取 request 请求内容
     *
     * @param request request
     * @return {String}
     */
    public String getRequestContent(HttpServletRequest request) {
        try {
            //HttpServletRequest的getQueryString方法返回请求的查询字符串（即URL中？后面的部分）
            String queryString = request.getQueryString();
            if (StrUtil.isNotBlank(queryString)) {
                return Arrays.stream(queryString.split(HTML_AMPERSAND + "|" + AMPERSAND))
                        .map(param -> {
                            try {
                                return URLDecoder.decode(param, Constant.UTF_8);
                            } catch (UnsupportedEncodingException e) {
                                return param;
                            }
                        })
                        .collect(Collectors.joining(AMPERSAND));
            }
            String charEncoding = Optional.ofNullable(request.getCharacterEncoding()).orElse(Constant.UTF_8);
            byte[] buffer = getBodyBytes(request);
            if(ArrayUtil.isEmpty(buffer)){
                return buildParamString(request);
            }
            String str = new String(buffer, charEncoding).trim();
            if(ServletXUtil.isJsonRequest(request)){
                str = JsonUtil.compactJson(str);
            }
            return StrUtil.isNotBlank(str) ? str : buildParamString(request);
        } catch (Exception ex) {
            ex.printStackTrace();
            return StringPool.EMPTY;
        }
    }

    private String buildParamString(HttpServletRequest request) {
        return request.getParameterMap().entrySet().stream()
                .flatMap(entry -> Arrays.stream(entry.getValue())
                        .map(value -> entry.getKey() + EQUALS + value))
                .collect(Collectors.joining(AMPERSAND));
    }

}
